/*
6.基于FFMPEG+SDL2播放音频
https://blog.csdn.net/u011003120/article/details/81950045
利用SDL2库实现音频的播放（一）	
http://blog.sina.com.cn/s/blog_825ad93f0102yvov.html

gcc -m32 -o demo audioDecodePlay1.c -lavformat -lavcodec -lswscale -lavutil -lswresample -lSDL2 -lm -lpthread -lz `pkg-config --cflags --libs sdl2`
*/

#include <stdio.h>
#include <assert.h>
#include <SDL2/SDL.h>
//#include <SDL_thread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/samplefmt.h"
#include "libswresample/swresample.h"
#include "libavutil/audio_fifo.h"

#define false 0

Uint32  audio_len;//每音频帧的长度
Uint8  *audio_pos;//目前接收播放位置
AVAudioFifo* audiofifo = NULL;//
int ct = 0;
long start_time, end_time;

//音频回调函数   用户数据   要填充的音频缓存区头指针  缓存区大小
void fill_audio(void *udata, Uint8 *stream, int len)
{
	SDL_memset(stream, 0, len);//初始化缓存区
	if (audio_len == 0)
		return;
	len = (len>audio_len ? audio_len : len);  
	SDL_MixAudio(stream, audio_pos, len, SDL_MIX_MAXVOLUME);
	audio_pos += len;
	audio_len -= len;
	//SDL_Delay(20);
}

int main(int argc, char* argv[])
{
	AVFormatContext *pFormatCtx;
	int             i, audioStream;
	AVCodecContext  *pCodecCtx;
	AVCodec         *pCodec;
	AVPacket        *packet;
	AVFrame         *pFrame;
	SDL_AudioSpec wanted_spec;
	int ret;
	uint32_t len = 0;
	int64_t in_channel_layout;
	struct SwrContext *au_convert_ctx;
	FILE *pFile = NULL;
	char url[] = "/home/jim/video_files/s320.mp4";
	//char url[] = "c:\\2.wma";
	 
	start_time = 0;//GetTickCount();
	//初始化
	av_register_all();
	avformat_network_init();
	pFormatCtx = avformat_alloc_context();
	if (avformat_open_input(&pFormatCtx, url, NULL, NULL) != 0) 
	{
		printf("Couldn't open input stream.\n");
		return -1;
	}
	if (avformat_find_stream_info(pFormatCtx, NULL)<0)
	{
		printf("Couldn't find stream information.\n");
		return -1;
	}
	av_dump_format(pFormatCtx, 0, url, false);//打印基础信息
	// Find the first audio stream
	audioStream = -1;
	for (i = 0; i < pFormatCtx->nb_streams; i++)
	{
		if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) 
		{
			audioStream = i;
			break;
		}
	}

	if (audioStream == -1) 
	{
		printf("Didn't find a audio stream.\n");
		return -1;
	}
	// Get a pointer to the codec context for the audio stream
	pCodecCtx = avcodec_alloc_context3(NULL);
	avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[audioStream]->codecpar);
	// Find the decoder for the audio stream
	pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
	if (pCodec == NULL) 
	{
		printf("Codec not found.\n");
		return -1;
	}
	//打印一些信息：
	printf("音频流标识符：%d\n", pFormatCtx->streams[audioStream]->index);
	printf("音频流长度：%d微秒\n", pFormatCtx->streams[audioStream]->duration);
	printf("音频时长：%d微秒\n", pFormatCtx->streams[audioStream]->duration);
	printf("音频采样率：%d\n", pFormatCtx->streams[audioStream]->codecpar->sample_rate);
	printf("音频信道数目：%d\n", pFormatCtx->streams[audioStream]->codecpar->channels);
	//printf("音频帧率：%d\n",pFormatCtx->streams[audioStream]->avg_frame_rate.den  );
	//int frame_rate = stream->avg_frame_rate.num / stream->avg_frame_rate.den;//每秒多少帧 / pFormatCtx->streams[audioStream]->avg_frame_rate.den
	// Open codec
	if (avcodec_open2(pCodecCtx, pCodec, NULL)<0) 
	{
		printf("Could not open codec.\n");
		return -1;
	}
	packet = (AVPacket *)av_malloc(sizeof(AVPacket));
	av_init_packet(packet);
	int out_framesize = 1024;//nb_samples: AAC-1024 MP3-1152 一般都是aac居多
	//音频输出格式设置out_XXX
	uint64_t out_channel_layout = AV_CH_LAYOUT_STEREO;
	int out_nb_samples = out_framesize;
	//AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
	int out_sample_rate = 44100;// pCodecCtx->sample_rate;
	int out_channels = av_get_channel_layout_nb_channels(out_channel_layout);
	//Out Buffer Size
	int out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, AV_SAMPLE_FMT_S16, 1);
	uint8_t ** audio_data_buffer = NULL;//存储转换后的数据，再编码AAC   二维数组data[0],data[1]
	audiofifo = av_audio_fifo_alloc(AV_SAMPLE_FMT_S16, out_channels, 1);
	pFrame = av_frame_alloc();

	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) 
	{
		printf("Could not initialize SDL - %s\n", SDL_GetError());
		return -1;
	}
	//SDL_AudioSpec
	wanted_spec.freq = out_sample_rate;//音频采样率
	wanted_spec.format = AUDIO_S16SYS;//采样精度
	wanted_spec.channels = out_channels;//音频频道数
	wanted_spec.silence = 0;//不静音
	wanted_spec.samples = out_nb_samples;//AAC 1024
	wanted_spec.callback = fill_audio;//音频回调函数 音频播放内部自动开线程循环调用
	wanted_spec.userdata = pCodecCtx;//用户数据
	if (SDL_OpenAudio(&wanted_spec, NULL)<0) 
	{
		printf("can't open audio.\n");
		return -1;
	}
	//FIX:Some Codec's Context Information is missing
	in_channel_layout = av_get_default_channel_layout(pCodecCtx->channels);
	au_convert_ctx = swr_alloc();
	au_convert_ctx = swr_alloc_set_opts(au_convert_ctx, 
	out_channel_layout,//输出
	AV_SAMPLE_FMT_S16, //编码前你希望的格式
	out_sample_rate,//输出
	in_channel_layout, //输入
	pCodecCtx->sample_fmt,//PCM源文件的采样格式 
	pCodecCtx->sample_rate, //输入
	0, NULL);
	swr_init(au_convert_ctx);
	 
	SDL_PauseAudio(0);//开始播放音频 应该是和下面的循环读取编解码并行线程
	AVFrame* audio_frame = av_frame_alloc();
	//循环读取音频帧并且编解码（发送包接收帧）
	while (av_read_frame(pFormatCtx, packet) >= 0)
	{
		if (packet->stream_index != audioStream) 
		{
			continue;
		}

		int ret = avcodec_send_packet(pCodecCtx, packet);//发送 包
		if (ret != 0)
		{
			printf("%s\n", "error");
		}
		while (avcodec_receive_frame(pCodecCtx, pFrame) == 0)
		{//接收 帧
			av_samples_alloc_array_and_samples(&audio_data_buffer,
			NULL, out_channels,
			pFrame->nb_samples, 
			AV_SAMPLE_FMT_S16, 1);
			int convert_size = swr_convert(au_convert_ctx,  
			audio_data_buffer,
			pFrame->nb_samples,
			(const uint8_t**)pFrame->data,
			pFrame->nb_samples);
			ret = av_audio_fifo_realloc(audiofifo, av_audio_fifo_size(audiofifo) + convert_size);
			if (ret < 0) 
			{
				printf("av_audio_fifo_realloc error\n");
				return -1;
			}
			if (av_audio_fifo_write(audiofifo, (void **)audio_data_buffer, convert_size) < convert_size) 
			{
				printf("av_audio_fifo_write error\n");
				return -1;
			}
			while (av_audio_fifo_size(audiofifo) >= out_framesize) 
			{
				int frame_size = FFMIN(av_audio_fifo_size(audiofifo), out_framesize);
				audio_frame->nb_samples = frame_size;
				audio_frame->channel_layout = out_channel_layout;
				audio_frame->format = AV_SAMPLE_FMT_S16;
				audio_frame->sample_rate = out_sample_rate;
				av_frame_get_buffer(audio_frame, 0);
				if (av_audio_fifo_read(audiofifo, (void **)audio_frame->data, frame_size) < frame_size) 
				{
					printf("av_audio_fifo_read error\n");
					return -1;
				}
				if (wanted_spec.samples != frame_size) 
				{
					SDL_CloseAudio();
					out_nb_samples = frame_size;
					out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples, AV_SAMPLE_FMT_S16, 1);
					wanted_spec.samples = out_nb_samples;
					SDL_OpenAudio(&wanted_spec, NULL);
				}
				while (audio_len>0)//Wait until finish
					SDL_Delay(1);

				audio_len = out_buffer_size;
				audio_pos = *audio_frame->data;
				 
				//SDL_Delay(40);
 
				if (++ct == 30)
				{
					ct = 0;
					end_time = 0;//GetTickCount();
					printf("音频帧率 %f \n", 30000.0 / (end_time - start_time));
					start_time = end_time;
				}
			} // end while
		} // end while
	}
	av_packet_unref(packet);

	swr_free(&au_convert_ctx);
	 
	 
	SDL_CloseAudio();//Close SDL
	SDL_Quit();
 
	if (audiofifo)
	{
		av_audio_fifo_free(audiofifo);
	}
	 
	avcodec_close(pCodecCtx);
	avformat_close_input(&pFormatCtx);
 
return 0;
}

